﻿package com.hsq.commons.util.beans;

import java.beans.PropertyEditor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.hsq.commons.util.annotation.Param;
import com.hsq.commons.util.cache.Cache;
import com.hsq.commons.util.cache.CacheValueBuilder;



/**
 * 
 * <p>Title: Bean工具类</p>
 *
 * <p>description:提供对Bean的一些处理方法.</p>
 * 
 * <p>@version: 1.0</p>
 * 
 * <p>@author: tangjiaping</p>
 * 
 * <p>@date: Feb 10, 2009</p>
 * 
 * <p>Company: 广州移盟数字传媒科技有限公司</p>
 */
@SuppressWarnings("unchecked")
public  class BeanUtils {

	private static final Log log = LogFactory.getLog(BeanUtils.class);
	
	private BeanUtils() {
	}

	/**
	 * Bean的信息的缓存
	 */
	private static final Cache<Class, BeanInfo> beans = new Cache(
			new CacheValueBuilder<Class, BeanInfo>() {
				public BeanInfo getValue(Class type) {
					return new BeanInfo(type);
				}
			});

	/**
	 * 对象的单一实例
	 */
	private static final Cache<Class, Object> instances = new Cache(
			new CacheValueBuilder<Class, Object>() {
				public Object getValue(Class type) {
					return newInstance(type);
				}
			});

	/**
	 * 获得了一个类的Bean相关信息
	 * 
	 * @param type
	 *            Class Bean的数据类型
	 * @return BeanInfo Bean的相关信息
	 */
	public static BeanInfo getBeanInfo(Class type) {
		return beans.get(type);
	}

	/**
	 * 使用参数注解来设定bean的属性值. <br>
	 * 要求bean必须有与参数名称相同的属性，并且方法参数为字符串.
	 * 
	 * @param bean
	 *            Object Bean的实例
	 * @param params
	 *            Param[] 参数注解
	 */
	public static void initBean(Object bean, Param[] params) {
		if (params != null && params.length > 0) {
			Class c = bean.getClass();
			for (Param param : params) {
				String name = param.name().substring(0, 1).toUpperCase()+ param.name().substring(1);
				Method m = null;
				try {
					m = c.getMethod("set" + name, String.class);
				} catch (Exception ex) // 查找set方法失败
				{
					log.error("Inject property '" + param.name()
							+ "' of class " + c.getName()
							+ " error , it's required a method set" + name
							+ "(java.lang.String) .", ex);
				}
				try {
					m.setAccessible(true);
					m.invoke(bean, param.value());
				} catch (Exception ex) // 方法调用失败
				{
					log.error("Inject property '" + param.name()
							+ "' of class " + bean.getClass()
							+ " error , property value is '" + param.value()
							+ "' .", ex);
				}
			}
		}
	}

	/**
	 * 设定对象的属性值. <br>
	 * 属性必须有set方法.
	 * 
	 * @param object
	 *            Object 目标实例.
	 * @param propertyName
	 *            String 属性名称
	 * @param propertyValue
	 *            Object 属性值
	 */
	public static void set(Object object, String propertyName,
			Object propertyValue) {
		BeanInfo bean = getBeanInfo(object.getClass());
		BeanProperty bp = bean.getProperty(propertyName);
		if (bp == null) // 属性不存在
		{
			throw new NullPointerException("Property '" + propertyName
					+ "' not found in class " + object.getClass().getName());
		}
		Method m = bp.getWriteMethod();
		if (m == null) // 写入方法不存在
		{
			log.error("Write method not found in property '"
					+ propertyName + "' not found in class "
					+ object.getClass().getName());
		}
		try {
			m.invoke(object, propertyValue);
		} catch (Exception ex) // 方法调用失败
		{
			log.error("Set property '" + propertyName
					+ "' of class " + object.getClass().getName()
					+ " failed , value is '" + propertyValue + "'", ex);
		}
	}

	/**
	 * 获取对象的属性值
	 * 
	 * @param object
	 *            Object 目标对象(不能为null).
	 * @param propertyName
	 *            String 属性名称(不能为null)
	 * @return Object 属性值
	 */
	public static Object get(Object object, String propertyName) {
		BeanProperty bp = getBeanInfo(object.getClass()).getProperty(
				propertyName);
		if (bp == null) // 属性不存在
		{
			throw new NullPointerException("Property '" + propertyName
					+ "' not found in class " + object.getClass().getName());
		}
		Method m = bp.getReadMethod();
		if (m == null) // 读取方法不存在
		{
			log.error("Read method not found in property '"
					+ propertyName + "' not found in class "
					+ object.getClass().getName());
		}
		try {
			return m.invoke(object);
		} catch (Exception ex) // 方法调用失败
		{
			log.error(
					"Get property '" + propertyName + "' of class "
							+ object.getClass().getName() + " failed .", ex);
		}
		return null;
	}

	/**
	 * 获取一组对象的同一属性值. <br>
	 * 要求目标对象数组componentTyp必须有属性propertyName
	 * 
	 * @param objects
	 *            Object[] 目标对象数组
	 * @param propertyName
	 *            String 属性名称
	 * @return Object[] 属性值数组
	 */
	public static Object[] get(Object[] objects, String propertyName) {
		Class type = objects.getClass().getComponentType();
		BeanProperty bp = getBeanInfo(type).getProperty(propertyName);
		if (bp == null) // 属性不存在
		{
			throw new NullPointerException("Property '" + propertyName
					+ "' not found in class " + type.getName());
		}
		Method m = bp.getReadMethod();
		if (m == null) // 读取方法不存在
		{
			log.error("Read method not found in property '"
					+ propertyName + "' not found in class " + type.getName());
		}
		Object[] rs = new Object[objects.length];
		try {
			for (int i = 0; i < objects.length; i++) {
				rs[i] = m.invoke(objects[i]);
			}
			return rs;
		} catch (Exception ex) // 方法调用失败
		{
			log.error("Get property '" + propertyName
					+ "' of class " + type.getName() + " failed .", ex);
		}
		return null;
	}

	/**
	 * 通过类名得到类
	 * 
	 * @param className
	 *            String 类名
	 * @return Class 类
	 */
	public static Class forName(String className) {
		try {
			return Class.forName(className);
		} catch (ClassNotFoundException ex) {
			log.error(ex);
		}
		return null;
	}

	/**
	 * 通过类名得到类的实例. <br>
	 * 要求类是公开的，并且有一个公开的无参数的构造方法.
	 * 
	 * @param className
	 *            String 类名
	 * @return Object 类的实例
	 */
	public static Object newInstance(String className) {
		return newInstance(forName(className));
	}

	/**
	 * 构造对象的实例
	 * 
	 * @param type
	 *            Class 对象的数据类型
	 * @return T 对象的实例
	 */
	public static <T> T newInstance(Class<T> type) {
		assert type != null : "Param type is null .";
		try {
			return type.newInstance();
		} catch (Exception ex) {
			log.error("Class " + type.getName()
					+ " required a no params and public constructor .", ex);
		}
		return null;
	}

	/**
	 * 获得一个对象的单一实例
	 * 
	 * @param type
	 *            Class
	 * @return T
	 */
	public static <T> T getInstance(Class<T> type) {
		return (T) instances.get(type);
	}

	/**
	 * 构造对象的实例
	 * 
	 * @param type
	 *            Class 对象的数据类型
	 * @param args
	 *            Object[] 构造参数
	 * @return T 对象的实例
	 */
	public static <T> T newInstance(Class<T> type, Object... args) {
		Class[] argsType = null;
		if (args != null) {
			argsType = new Class[args.length];
			for (int i = 0; i < args.length; i++) {
				argsType[i] = (args[i] == null) ? null : args.getClass();
			}
		}
		try {
			Constructor<T> con = type.getConstructor(argsType);
			return con.newInstance(args);
		} catch (NoSuchMethodException ex) // 构造方法未找到
		{
			log.error(getConstructorDeclare(type, argsType)
					+ " not found in class " + type.getName(), ex);
		} catch (Exception ex) {
			log.error(getConstructorDeclare(type, argsType)
					+ " invoke failed .", ex);
		}
		return null;
	}

	/**
	 * 构造方法描述
	 * 
	 * @param type
	 *            Class
	 * @param argsType
	 *            Class[]
	 * @return String
	 */
	private static String getConstructorDeclare(Class type, Class[] argsType) {
		StringBuilder rs = new StringBuilder("Constructor 'public "
				+ type.getSimpleName() + " (");
		if (argsType != null) {
			for (Class c : argsType) {
				rs.append(c.getName());
				rs.append(',');
			}
			rs.deleteCharAt(rs.length() - 1);
		}
		rs.append(')');
		return rs.toString();
	}

	/**
	 * 通过注解得到初始化后的属性编辑器
	 * 
	 * @param editor
	 *            Editor 属性编辑器注解
	 * @return PropertyEditor 初始化后的属性编辑器
	 */
	public static PropertyEditor getEditor(Editor editor) {
		PropertyEditor rs = newInstance(editor.value());
		initBean(rs, editor.params());
		return rs;
	}

	/**
	 * 检查一个类是否抽象类或是接口
	 * 
	 * @param type
	 *            Class 数据类型
	 * @return boolean 是抽象类或是接口时返回true
	 */
	public static boolean isAbstractOrInterface(Class type) {
		if (type.isInterface()) {
			return true;
		}
		int modifier = type.getModifiers();
		if (Modifier.isAbstract(modifier)) {
			return true;
		}
		return false;
	}

	/**
	 * 复制两个对象相同的属性
	 * 
	 * @param source
	 *            Object 源对象
	 * @param target
	 *            Object 目标对象
	 */
	public static void copyProperties(Object source, Object target) {
		Map<String, BeanProperty> sourceProp = getBeanInfo(source.getClass())
				.getProperties();
		try {
			Map<String, BeanProperty> targetProp = getBeanInfo(
					target.getClass()).getProperties();
			for (String p : sourceProp.keySet()) {
				BeanProperty bp = sourceProp.get(p);
				if (bp.getReadMethod() != null) {
					BeanProperty b = targetProp.get(p);
					if (b != null && b.getWriteMethod() != null
							&& b.getType().isAssignableFrom(bp.getType())) {
						Object value = bp.getReadMethod().invoke(source);
						b.getWriteMethod().invoke(target, value);
					}
				}
			}
		} catch (Exception ex) {
			log.error(ex);
		}
	}

	public static void setFieldValue(Object target, String fname, Class ftype,
			Object fvalue) {
		if (target == null
				|| fname == null
				|| "".equals(fname)
				|| (fvalue != null && !ftype
						.isAssignableFrom(fvalue.getClass()))) {
			log.error("_target:"+target+"_fname:"+fname+"_ftype:"+ftype+"_fvalue:"+fvalue+"_!ftype.isAssignableFrom(fvalue.getClass())):"+(!ftype.isAssignableFrom(fvalue.getClass()))+"_________return___error___");
			return;
		}
		
		Class clazz = target.getClass();
		try {
			Method method = clazz.getDeclaredMethod("set"
					+ Character.toUpperCase(fname.charAt(0))
					+ fname.substring(1), ftype);
			if (!Modifier.isPublic(method.getModifiers())) {
				method.setAccessible(true);
			}
			method.invoke(target, fvalue);
		} catch (Exception me) {
			try {
				Field field = clazz.getDeclaredField(fname);
				if (!Modifier.isPublic(field.getModifiers())) {
					field.setAccessible(true);
				}
				field.set(target, fvalue);
			} catch (Exception fe) {
				log.error(me);
				fe.printStackTrace();
			}
			
		}
	}
}
